/*
 * server.h
 *
 * 	Ex: 5
 *
 *  Created on: Jun 6, 2012
 *      Author: Ariel Ayash
 */

#ifndef SERVER_H_
#define SERVER_H_

#include "client_context.h"
#include "common_defs.h"
#include "protocol.h"

#include <map>
#include <cstring>
#include <fstream>
#include <list>
#include <cassert>

#define INVALID_SOCKET (-1)

typedef struct
{
	uint socket;
	bool first_time;
	uint32_t already_sent;
	out_msg_t *msg;

} deferred_msg_t;

class NamesCmp
{

// comparator class that compares two name  
public:

	bool operator() ( const char*  lhs, const char*  rhs ) const
	{
		assert( NULL != lhs && NULL != rhs );
		char lch,rch;

		for ( uint i = 0; i <  MAXIMAL_CLIENT_NAME_LENGTH; ++i )
		{
			if ( '\0' == lhs[i] && '\0' == rhs[i] )
			{
				return false;
			}

			lch = tolower(lhs[i]);
			rch = tolower(rhs[i]);
			if ( lch > rch )
			{
				return false;
			}

			if( lch < rch )
			{
				return true;
			}
		}

		assert( 0 ); // should never be here
		return false;
	}
};


class Server
{

public:

	explicit Server( int port, result_t &result );

	// start the server's functionality 
	void server_start();

	~Server();

private:

	//	--------------------------------------------------------------------------------->
	//						 connectivity function
	//	--------------------------------------------------------------------------------->

	// perform the opening of the socket
	result_t open_socket( uint32_t port );

	// accept a new client that try to connect
	void accept_new_client();
	
	// send all the deferred messages that can be send - those their socket is free
	void send_deferred_msgs( fd_set *ready, uint &num_to_handle);
	
	// close clients that their sockets got exception
	void check_for_exception(  fd_set *exception_set, uint & num_to_handle );

	// get messages that are waiting on the given socket list
	void get_msgs( fd_set *waiting, uint &num_to_handle);

	// close the socket and clear all the databases
	void close_server();
	
	
	//	--------------------------------------------------------------------------------->
	//							Utilities functions
	//	--------------------------------------------------------------------------------->
	
	// send a message to the given client's socket
	void send_msg(uint client_socket,
				  messages_type_t type,
				  response_status_t status,
				  std::string & buffer );
				  
	/*
	 * return the socket that belong to the client with the given name
	 * return INVALID_SOCKET in case no client with the given name exists.
	 */
	int get_socket_by_name( const char *name );
	
	/*
	 * return the context of the client that his socket match the given one
	 * return NULL in case there is no client with the given socket number
	 */
	ClientContext* get_context_by_socket( uint client_socket  );
	
	
	//	--------------------------------------------------------------------------------->
	//							Request handling functions
	//	--------------------------------------------------------------------------------->
	
	// parse a message that was received from client
	result_t parse_message( uint client_socket, in_msg_t *msg );

	void set_new_client_name( uint client_socket, const char *name );

	// remove the given client from the databases - this method doesn't remove it from the map
	void close_connection_with_client( uint client_socket, bool explicit_close = false );

	// add client_socket to name_followed followers' list
	void add_to_follow_list( uint client_socket, const char *name_followed );

	// remove client_socket from name_followed followers' list
	void remove_from_follow_list( uint client_socket, const char *name_followed );

	// twit a message "msg" by client "client_socket"
	void twit( uint client_socket, const char *msg );

	void send_direct_msg( uint client_socket,
						  const char *recipient_name,
						  const char *msg );

	void send_public_announcement( uint client_socket, const char *msg );

	// send a list of all known clients to client client_socket
	void send_clients_list( uint client_socket );

	/*
	 *  print error message to log file. in case to_errno is set - print the message using
	 *  perror otherwise print to standard error
	 */
	void print_err_msg( const char *err_message, bool to_errno = false);


	//	------------------------------------------------------------>
	//						Server fields
	//	------------------------------------------------------------>
	int _hsock;											// server socket

	std::ofstream _log_file;							// log file handle

	std::map< uint, ClientContext* >	_clients;		// known clients

	std::map<const char *, uint , NamesCmp>	_names;		/*<	map between client's name to
															his socket number 			>*/

	std::list< deferred_msg_t > 	_deferred_msg;		// messages that should be sent

	bool _is_running;									// flag that indicate running status

	uint _max_file_desc_num;							/*< maximal file descriptor number
															that in the server
															(include sockets) this number
															will be used when calling
															to select()					>*/

	fd_set _in_set;										/*< set that contains all the known
															clients. will be used to wait
															for messages from them.		>*/
};


#endif /* SERVER_H_ */
